<?php

namespace Biz\service\bizAd;

use Biz\model\bizAd\AdRechargeOrder as mAdRechargeOrder;
use Biz\model\bizAd\AdWallet as mAdWallet;
use Biz\model\bizAd\AdWalletLog as mAdWalletLog;
use Biz\pockets\constInterface\BizAd as iBizAd;
use Doraemon\tools\Log;

/**
 * @method static bool recharge($uid, $utype, $amount, $opUid, $opUidType, $orderUpdate)
 * @method static bool deduct($uid, $utype, $amount, $planId, $planName)
 * @method static bool unlockAmount($uid, $utype, $refundAmount, $unlockAmount, $planId, $planName)
 * @method static bool cashOut($uid, $utype, $amount, $adminId, $transNo)
 */
class AdWallet implements iBizAd
{
    const ERROR_CODE_GET_WALLET_FAIL = 40000; //获取建钱包失败
    const ERROR_CODE_CREATE_WALLET_FAIL = 40001; //创建钱包失败
    const ERROR_CODE_RECHARGE_FAIL = 40002; //充值失败
    const ERROR_CODE_ADDLOG_FAIL = 40003; //添加操作记录失败
    const ERROR_CODE_UPDATEORDERPAY_FAIL = 40004; //更新订单状态为已支付失败
    const ERROR_CODE_DEDUCT_FAIL = 40005; //钱包扣款失败
    const ERROR_CODE_CASH_OUT_BALANCE_NOT_ENOUGH = 40006; //提现钱包余额不足
    const ERROR_CODE_CASH_OUT_FAIL = 40007; //提现失败
    const ERROR_CODE_DEDUCT_BALANCE_NOT_ENOUGH = 40008; //消费扣款余额不足
    const ERROR_CODE_OP_LOG_EXSIT = 40009; //已存在操作记录

    /**
     * 获取钱包余额
     * @param $uid
     * @return float
     */
    public static function getBalance($uid)
    {
        $balance = 0.00;
        $wallet = mAdWallet::getWallet($uid);
        return $wallet ? $wallet['balance'] : $balance;
    }

    /**
     * 获取钱包在途金额
     * @param $uid
     * @return float
     */
    public static function getLockAmount($uid)
    {
        $lockAmount = 0.00;
        $wallet = mAdWallet::getWallet($uid);
        return $wallet ? $wallet['lock_amount'] : $lockAmount;
    }

    /**
     * 获取钱包信息
     * @param $uid
     * @return float
     */
    public static function getWallet($uid)
    {
        return mAdWallet::getWallet($uid);
    }

    /**
     * 钱包充值
     * @param $uid
     * @param $utype
     * @param $amount
     * @param $opUid
     * @param $opUidType
     * @param $orderUpdate
     * @return array
     * @throws \Exception
     */
    private static function _recharge($uid, $utype, $amount, $opUid, $opUidType, $orderUpdate)
    {
        if ($amount <= 0) {
            throw new \Exception('参数错误！');
        }

        $wallet = mAdWallet::getWallet($uid);

        if (!$wallet) {
            $wallet = mAdWallet::createWallet($uid, $utype);
            if (!$wallet) {
                throw new \Exception('创建钱包失败！', self::ERROR_CODE_CREATE_WALLET_FAIL);
            }
        }

        $dsn = $orderUpdate['dsn'];

        if (mAdWalletLog::hasOpLog($uid, mAdWalletLog::OP_TYPE_RECHARGE, $dsn)) {
            throw new \Exception('记录已存在！', self::ERROR_CODE_OP_LOG_EXSIT);
        }

        $walletId = $wallet['id'];

        $balance = $wallet['balance'];

        $version = $wallet['version'];

        $lockAmount = $wallet['lock_amount'];

        $newBalance = bcadd($balance, $amount, 2);

        $newVersion = $version + 1;

        $funcRechargeArgs = [$walletId, $balance, $newBalance, $version, $newVersion];

        $remark = mAdWalletLog::getRemark(mAdWalletLog::OP_TYPE_RECHARGE);

        $funcAddLogArgs = [
            $walletId,
            $uid,
            $utype,
            mAdWalletLog::OP_TYPE_RECHARGE,
            $balance,
            $amount,
            $newBalance,
            $opUidType,
            $opUid,
            $dsn,
            $remark,
            $lockAmount,
            $lockAmount,
            $newVersion
        ];

        $funcOrderUpdateArgs = [
            $dsn,
            $orderUpdate['other_sn'],
            $orderUpdate['pay_status'],
            $orderUpdate['pay_time'],
            $orderUpdate['pay_platform'],
            $orderUpdate['amount'],
            $orderUpdate['order_status']
        ];

        if (isset($orderUpdate['trade_proof'])) {
            $funcOrderUpdateArgs[] = $orderUpdate['trade_proof'];
        }

        $errorData = [
            'code' => 0,
            'msg' => '',
            'sql' => '',
        ];

        $rechargeResult = mAdWallet::action(function () use (
            $funcRechargeArgs,
            $funcAddLogArgs,
            $funcOrderUpdateArgs,
            &$errorData
        ) {

            if (!mAdWallet::recharge(...$funcRechargeArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_RECHARGE_FAIL,
                    'msg' => '更新充值金额失败',
                    'args' => $funcRechargeArgs,
                ];
                return false;
            }

            if (!mAdWalletLog::addLog(...$funcAddLogArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_ADDLOG_FAIL,
                    'msg' => '添加充值记录失败',
                    'args' => $funcAddLogArgs,
                ];
                return false;
            }

            if (!mAdRechargeOrder::updatePayStatus(...$funcOrderUpdateArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_UPDATEORDERPAY_FAIL,
                    'msg' => '更新订单状态为已支付失败',
                    'args' => $funcOrderUpdateArgs,
                ];
                return false;
            }

            return true;
        });

        if ($rechargeResult) {

            $message = [
                'action' => mAdWalletLog::OP_TYPE_RECHARGE,
                'uid' => $uid,
                'utype' => $utype,
                'amount' => $amount,
                'balance' => $newBalance,
                'lock_amount' => $lockAmount,
                'op_utype' => $opUidType,
                'op_uid' => $opUid,
                'op_rel_id' => $dsn,
                'title' => $remark,
                'time' => date('Y-m-d H:i:s', time()),
                'version' => $newVersion,
            ];

            AdWalletMonitor::balanceChangeRemind($message);

            return [true, null];
        }

        $errorData['sql'] = [
            'recharge' => mAdWallet::log(),
            'addLog' => mAdWalletLog::log(),
            'updatePayStatus' => mAdRechargeOrder::log(),
        ];

        return [false, $errorData];

    }

    /**
     * @param $uid
     * @param $utype
     * @param $amount
     * @param $planId
     * @param $planName
     * @return array|false
     */
    private static function _deduct($uid, $utype, $amount, $planId, $planName)
    {
        if ($amount < 0) {
            throw new \Exception('参数错误！');
        }

        $wallet = mAdWallet::getWallet($uid);

        if (!$wallet) {
            throw new \Exception('获取钱包失败', self::ERROR_CODE_GET_WALLET_FAIL);
        }

        $walletId = $wallet['id'];

        $balance = $wallet['balance'];

        if ($amount > $balance) {
            throw new \Exception('余额不足', self::ERROR_CODE_DEDUCT_BALANCE_NOT_ENOUGH);
        }

        if (mAdWalletLog::hasOpLog($uid, mAdWalletLog::OP_TYPE_CONSUME, $planId)) {
            throw new \Exception('记录已存在！', self::ERROR_CODE_OP_LOG_EXSIT);
        }

        $lockAmount = $wallet['lock_amount'];

        $version = $wallet['version'];

        $newVersion = $version + 1;

        $newBalance = bcsub($balance, $amount, 2);

        $newLockAmount = bcadd($lockAmount, $amount, 2);

        $funcDeductArgs = [$walletId, $balance, $newBalance, $lockAmount, $newLockAmount, $version, $newVersion];

        $remark = mAdWalletLog::getRemark(mAdWalletLog::OP_TYPE_CONSUME, $planName);

        $funcAddLogArgs = [
            $walletId,
            $uid,
            $utype,
            mAdWalletLog::OP_TYPE_CONSUME,
            $balance,
            $amount,
            $newBalance,
            mAdWalletLog::OP_UID_TYPE_USER,
            $uid,
            $planId,
            $remark,
            $newLockAmount,
            $lockAmount,
            $newVersion
        ];

        $errorData = [
            'code' => 0,
            'msg' => '',
            'sql' => '',
        ];

        $deductResult = mAdWallet::action(function () use ($funcDeductArgs, $funcAddLogArgs, &$errorData) {

            if (!mAdWallet::deduct(...$funcDeductArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_DEDUCT_FAIL,
                    'msg' => '钱包扣款失败',
                    'args' => $funcDeductArgs,
                ];
                return false;
            }

            if (!mAdWalletLog::addLog(...$funcAddLogArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_ADDLOG_FAIL,
                    'msg' => '添加扣费记录失败',
                    'args' => $funcAddLogArgs,
                ];
                return false;
            }

            return true;
        });

        if ($deductResult) {

            $message = [
                'action' => mAdWalletLog::OP_TYPE_CONSUME,
                'uid' => $uid,
                'utype' => $utype,
                'amount' => $amount,
                'balance' => $newBalance,
                'lock_amount' => $newLockAmount,
                'op_utype' => mAdWalletLog::OP_UID_TYPE_USER,
                'op_uid' => $uid,
                'op_rel_id' => $planId,
                'title' => $remark,
                'time' => date('Y-m-d H:i:s', time()),
                'version' => $newVersion,
            ];

            AdWalletMonitor::balanceChangeRemind($message);

            return [true, null];
        }

        $errorData['sql'] = [
            'deduct' => mAdWallet::log(),
            'addLog' => mAdWalletLog::log(),
        ];

        return [false, $errorData];

    }


    /**
     * 结算在途金额
     * @param $uid
     * @param $utype
     * @param $refundAmount
     * @param $unlockAmount
     * @param $planId
     * @param $planName
     * @return array
     * @throws \Exception
     */
    private static function _unlockAmount($uid, $utype, $refundAmount, $unlockAmount, $planId, $planName)
    {
        if ($refundAmount < 0 || $unlockAmount < 0) {
            throw new \Exception('参数错误！');
        }

        $wallet = mAdWallet::getWallet($uid);

        if (!$wallet) {
            throw new \Exception('获取钱包失败', self::ERROR_CODE_GET_WALLET_FAIL);
        }

        $walletId = $wallet['id'];

        $balance = $wallet['balance'];

        $lockAmount = $wallet['lock_amount'];

        $version = $wallet['version'];

        $newBalance = bcadd($balance, $refundAmount, 2);

        $newVersion = $version + 1;

        $newLockAmount = bcsub($lockAmount, $unlockAmount, 2);

        if (mAdWalletLog::hasOpLog($uid, mAdWalletLog::OP_TYPE_RELEASE_LOCK_AMOUNT, $planId)) {
            throw new \Exception('记录已存在！', self::ERROR_CODE_OP_LOG_EXSIT);
        }

        $funcSettleUnockAmountArgs = [
            $walletId,
            $balance,
            $newBalance,
            $lockAmount,
            $newLockAmount,
            $version,
            $newVersion
        ];

        $remarkOnTheWay = mAdWalletLog::getRemark(mAdWalletLog::OP_TYPE_RELEASE_LOCK_AMOUNT, $planName);

        $remark = mAdWalletLog::getRemark(mAdWalletLog::OP_TYPE_REFUND, $planName);

        $funcOnTheWayAddLogArgs = [
            $walletId,
            $uid,
            $utype,
            mAdWalletLog::OP_TYPE_RELEASE_LOCK_AMOUNT,
            $balance,
            $unlockAmount,
            $newBalance,
            mAdWalletLog::OP_UID_TYPE_USER,
            $uid,
            $planId,
            $remarkOnTheWay,
            $newLockAmount,
            $lockAmount,
            $newVersion
        ];

        $funcAddLogArgs = [
            $walletId,
            $uid,
            $utype,
            mAdWalletLog::OP_TYPE_REFUND,
            $balance,
            $refundAmount,
            $newBalance,
            mAdWalletLog::OP_UID_TYPE_USER,
            $uid,
            $planId,
            $remark,
            $newLockAmount,
            $lockAmount,
            $newVersion
        ];

        $errorData = [
            'code' => 0,
            'msg' => '',
            'sql' => '',
        ];

        $nRet = mAdWallet::action(function () use (
            $funcSettleUnockAmountArgs,
            $funcOnTheWayAddLogArgs,
            $funcAddLogArgs,
            $refundAmount,
            &$errorData
        ) {
            if (!mAdWallet::settleLockAmount(...$funcSettleUnockAmountArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_ADDLOG_FAIL,
                    'msg' => '在途金额结算失败！',
                    'args' => $funcSettleUnockAmountArgs,
                ];
                return false;
            }

            if (!mAdWalletLog::addLog(...$funcOnTheWayAddLogArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_ADDLOG_FAIL,
                    'msg' => '添加在途金额结算记录失败！',
                    'args' => $funcOnTheWayAddLogArgs,
                ];
                return false;
            }

            if ($refundAmount > 0 && !mAdWalletLog::addLog(...$funcAddLogArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_ADDLOG_FAIL,
                    'msg' => '添加退还记录失败！',
                    'args' => $funcAddLogArgs,
                ];
                return false;
            }
            return true;
        });

        if ($nRet) {

            $messageSettle = [
                'action' => mAdWalletLog::OP_TYPE_RELEASE_LOCK_AMOUNT,
                'uid' => $uid,
                'utype' => $utype,
                'amount' => $unlockAmount,
                'balance' => $newBalance,
                'lock_amount' => $newLockAmount,
                'op_utype' => mAdWalletLog::OP_UID_TYPE_USER,
                'op_uid' => $uid,
                'op_rel_id' => $planId,
                'title' => $remarkOnTheWay,
                'time' => date('Y-m-d H:i:s', time()),
                'version' => $newVersion,
            ];

            AdWalletMonitor::balanceChangeRemind($messageSettle);

            if ($refundAmount > 0) {

                $messageRefund = [
                    'action' => mAdWalletLog::OP_TYPE_REFUND,
                    'uid' => $uid,
                    'utype' => $utype,
                    'amount' => $refundAmount,
                    'balance' => $newBalance,
                    'lock_amount' => $newLockAmount,
                    'op_utype' => mAdWalletLog::OP_UID_TYPE_USER,
                    'op_uid' => $uid,
                    'op_rel_id' => $planId,
                    'title' => $remark,
                    'time' => date('Y-m-d H:i:s', time()),
                    'version' => $newVersion,
                ];

                AdWalletMonitor::balanceChangeRemind($messageRefund);

            }

            return [true, null];
        }

        $errorData['sql'] = [
            'unlockAmount' => mAdWallet::log(),
            'addLog' => mAdWalletLog::log(),
        ];

        return [false, $errorData];

    }

    /**
     * @param $uid
     * @param $utype
     * @param $amount
     * @param $adminId
     * @param $transNo
     * @return array
     * @throws \Exception
     */
    private static function _cashOut($uid, $utype, $amount, $adminId, $transNo)
    {
        if ($amount <= 0) {
            throw new \Exception('参数错误！');
        }

        $wallet = mAdWallet::getWallet($uid);

        if (!$wallet) {
            throw new \Exception('获取钱包失败', self::ERROR_CODE_GET_WALLET_FAIL);
        }

        $walletId = $wallet['id'];

        $balance = $wallet['balance'];

        $version = $wallet['version'];

        $lockAmount = $wallet['lock_amount'];

        if ($amount > $balance) {
            throw new \Exception('余额不足', self::ERROR_CODE_CASH_OUT_BALANCE_NOT_ENOUGH);
        }

        if (mAdWalletLog::hasOpLog($uid, mAdWalletLog::OP_TYPE_CASHOUT, $transNo)) {
            throw new \Exception('记录已存在！', self::ERROR_CODE_OP_LOG_EXSIT);
        }

        $newBalance = bcsub($balance, $amount, 2);

        $newVersion = $version + 1;

        $funcCashOutArgs = [$walletId, $balance, $newBalance, $version, $newVersion];

        $remark = mAdWalletLog::getRemark(mAdWalletLog::OP_TYPE_CASHOUT);

        $funcAddLogArgs = [
            $walletId,
            $uid,
            $utype,
            mAdWalletLog::OP_TYPE_CASHOUT,
            $balance,
            $amount,
            $newBalance,
            mAdWalletLog::OP_UID_TYPE_OP,
            $adminId,
            $transNo,
            $remark,
            $lockAmount,
            $lockAmount,
            $newVersion
        ];

        $errorData = [
            'code' => 0,
            'msg' => '',
            'sql' => '',
        ];

        $cashOutResult = mAdWallet::action(function () use ($funcCashOutArgs, $funcAddLogArgs, &$errorData) {

            if (!mAdWallet::cashOut(...$funcCashOutArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_CASH_OUT_FAIL,
                    'msg' => '钱包提现失败',
                    'args' => $funcCashOutArgs,
                ];
                return false;
            }

            if (!mAdWalletLog::addLog(...$funcAddLogArgs)) {
                $errorData = [
                    'code' => self::ERROR_CODE_ADDLOG_FAIL,
                    'msg' => '添加提现记录失败',
                    'args' => $funcAddLogArgs,
                ];
                return false;
            }

            return true;
        });

        if ($cashOutResult) {

            $message = [
                'action' => mAdWalletLog::OP_TYPE_CASHOUT,
                'uid' => $uid,
                'utype' => $utype,
                'amount' => $amount,
                'balance' => $newBalance,
                'lock_amount' => $lockAmount,
                'op_utype' => mAdWalletLog::OP_UID_TYPE_OP,
                'op_uid' => $adminId,
                'op_rel_id' => $transNo,
                'title' => $remark,
                'time' => date('Y-m-d H:i:s', time()),
                'version' => $newVersion,
            ];

            AdWalletMonitor::balanceChangeRemind($message);

            return [true, null];
        }

        $errorData['sql'] = [
            'cashOut' => mAdWallet::log(),
            'addLog' => mAdWalletLog::log(),
        ];

        return [false, $errorData];

    }


    /**
     * 账单列表
     * @param $where
     * @param $index
     * @return array
     */
    public static function billList($where, $index = 0)
    {
        $list = mAdWalletLog::dataSelect($where, ['id', 'amount', 'op_rel_id', 'op_type', 'create_time', 'remark']);

        $returnData = [];

        if ($list && is_array($list)) {
            foreach ($list as $key => $row) {
                $index++;
                $returnData[$key]['id'] = $index;
                $returnData[$key]['dsn'] = $row['op_rel_id'];
                $returnData[$key]['create_time'] = date('Y-m-d', strtotime($row['create_time']));
                $returnData[$key]['remark'] = $row['remark'];
                $returnData[$key]['amount'] = $row['amount'];
                $returnData[$key]['op_type'] = $row['op_type'];
                $returnData[$key]['symbol_amount'] = mAdWalletLog::SYMBOLS[$row['op_type']] . $row['amount'];
                $returnData[$key]['log_id'] = $row['id'];
            }
        }

        return $returnData;
    }

    public static function __callStatic($method, $args)
    {
        $error = [];

        try {

            $callMethod = sprintf("_%s", $method);

            [$bRet, $error] = self::$callMethod(...$args);

            if (!$bRet) {
                throw new \Exception('业务处理失败');
            }

            return true;

        } catch (\Exception $e) {

            Log::error($e->getMessage(), [
                'error' => $error,
                'func' => $method,
                'params' => $args,
            ]);

            $message = [
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'call_method' => $method,
                'time' => date('Y-m-d H:i:s', time())
            ];

            AdWalletMonitor::bugAlert($message);

            return false;
        }
    }


}